// /////////////////////////////////////////////////////////////////////////////
//
// Name:            CameraComponent.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           Component wrapper of Irrlicht's ICameraSceneNode.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "CameraComponent.h"
#include "../../core/GameManager.h"


// CameraComponent class
// CameraComponent constructor (default).
CameraComponent::CameraComponent(Entity *parent, const vector3df &lookat)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
    {
       // Get pointers to needed sub-systems of the Game Manager.
       ISceneManager *pSceneMgr = GameManager::Instance()->getSceneManager();

       // Create the internal scene node.
       mCameraSN = pSceneMgr->addCameraSceneNode(0, parent->getAbsolutePosition(), lookat,
                                                 parent->getID());
       mSceneNode = mCameraSN;

       mCameraAnimator = NULL;

       // Set-up internal mechanism for collision detection.
       mTriSelector = NULL;
       mMetaSelector = pSceneMgr->createMetaTriangleSelector();
    }
}

// CameraComponent deconstructor.
CameraComponent::~CameraComponent()
{
    if(pParent != NULL)
    {
       mCameraSN->remove();
       mSceneNode = NULL;
    }
}

// Returns a direct pointer to the ICameraSceneNode.
ICameraSceneNode* CameraComponent::getCameraSceneNode()
{
    return mCameraSN;
}

// Binds the camera component's rotation to its target position and vice vera, or unbinds them.
void CameraComponent::bindTargetAndRotation(bool bound)
{
    mCameraSN->bindTargetAndRotation(bound);
}

// Queries if the camera component's rotation and its target position are bound together.
bool CameraComponent::getTargetAndRotationBinding() const
{
    return mCameraSN->getTargetAndRotationBinding();
}

// Gets the aspect ratio of the camera.
f32 CameraComponent::getAspectRatio() const
{
    return mCameraSN->getAspectRatio();
}

// Gets the value of the far plane of the camera.
f32 CameraComponent::getFarValue() const
{
    return mCameraSN->getFarValue();
}

// Gets the field of view of the camera.
f32 CameraComponent::getFOV() const
{
    return mCameraSN->getFOV();
}

// Gets the value of the near plane of the camera.
f32 CameraComponent::getNearValue() const
{
    return mCameraSN->getNearValue();
}

// Gets the current projection matrix of the camera.
const matrix4& CameraComponent::getProjectionMatrix() const
{
    return mCameraSN->getProjectionMatrix();
}

// Gets the current look at target of the camera.
const vector3df& CameraComponent::getTarget() const
{
    return mCameraSN->getTarget();
}

// Gets the up vector of the camera.
const vector3df& CameraComponent::getUpVector() const
{
    return mCameraSN->getUpVector();
}

// Gets the current view matrix of the camera.
const matrix4& CameraComponent::getViewMatrix() const
{
    return mCameraSN->getViewMatrix();
}

// Checks if the camera is orthogonal.
bool CameraComponent::getIsOrthogonal() const
{
    return mCameraSN->isOrthogonal();
}

// Sets this camera as the main camera.
void CameraComponent::setAsMainCamera()
{
    GameManager::Instance()->getSceneManager()->setActiveCamera(mCameraSN);
}

// Sets the aspect ratio (default: 4.0f / 3.0f).
void CameraComponent::setAspectRatio(f32 aspect)
{
    mCameraSN->setAspectRatio(aspect);
}

// Sets the value of the far clipping plane (default: 2000.0f).
void CameraComponent::setFarValue(f32 value)
{
    mCameraSN->setFarValue(value);
}

// Sets the field of view (Default: PI / 2.5f).
void CameraComponent::setFOV(f32 value)
{
    mCameraSN->setFOV(value);
}

// Sets the value of the near clipping plane. (default: 1.0f).
void CameraComponent::setNearValue(f32 value)
{
    mCameraSN->setNearValue(value);
}

// Sets the projection matrix of the camera.
void CameraComponent::setProjectionMatrix(const matrix4 &projection, bool isOrthogonal)
{
    mCameraSN->setProjectionMatrix(projection, isOrthogonal);
}

// Sets the rotation of the node.
void CameraComponent::setRotation(const vector3df &rotation)
{
    mCameraSN->setRotation(pParent->getAbsoluteRotation() + rotation);
}

// Sets the look at target of the camera.
void CameraComponent::setTarget(const vector3df &position)
{
    mCameraSN->setTarget(position);
}

// Sets the up vector of the camera.
void CameraComponent::setUpVector(const vector3df &position)
{
    mCameraSN->setUpVector(position);
}

// Sets the camera to normal mode.
void CameraComponent::setNormalMode()
{
    if(mCameraAnimator)
      mCameraSN->removeAnimator(mCameraAnimator);
}

// Sets the camera to FPS mode.
void CameraComponent::setFPSMode(f32 rotateSpeed, f32 moveSpeed, bool verticalMovement,
                                 f32 jumpSpeed)
{
    // Remove previous camera animator.
    if(mCameraAnimator)
      mCameraSN->removeAnimator(mCameraAnimator);

    // Add FPS camera animator.
    ISceneNodeAnimatorCameraFPS *animFPS = (ISceneNodeAnimatorCameraFPS*)GameManager::Instance()->
                                           getSceneManager()->getDefaultSceneNodeAnimatorFactory()->
                                           createSceneNodeAnimator("cameraFPS", mCameraSN);

    animFPS->setRotateSpeed(rotateSpeed);
    animFPS->setMoveSpeed(moveSpeed);
    animFPS->setVerticalMovement(verticalMovement);
    animFPS->drop();

    mCameraAnimator = animFPS;
}

// Sets the camera to Maya mode.
void CameraComponent::setMayaMode(f32 rotateSpeed, f32 zoomSpeed, f32 translationSpeed)
{
    // Remove previous camera animator.
    if(mCameraAnimator)
      mCameraSN->removeAnimator(mCameraAnimator);

    // Add Maya camera animator.
    ISceneNodeAnimatorCameraMaya *animMaya = (ISceneNodeAnimatorCameraMaya*)GameManager::Instance()->
                                           getSceneManager()->getDefaultSceneNodeAnimatorFactory()->
                                           createSceneNodeAnimator("cameraMaya", mCameraSN);

    animMaya->setRotateSpeed(rotateSpeed);
    animMaya->setMoveSpeed(translationSpeed);
    animMaya->setZoomSpeed(zoomSpeed);
    animMaya->drop();

    mCameraAnimator = animMaya;
}

// Serializes this object.
bool CameraComponent::onSerialization(SerializedAttributes *attributes)
{
    // Create a root.
    SerializedAttributes *root = new SerializedAttributes();

    // Serialize attributes inherited from SceneComponent.
    SceneComponent::onSerialization(root);

    // Serialize attributes.
    root->pushBool("asMainCamera", GameManager::Instance()->getSceneManager()->getActiveCamera() == mCameraSN);
    root->pushFloat("aspectRatio", getAspectRatio());
    root->pushFloat("farValue", getFarValue());
    root->pushFloat("FOV", getFOV());
    root->pushFloat("nearValue", getNearValue());
    root->pushVector3df("target", getTarget());
    root->pushVector3df("upVector", getUpVector());

    // Add root to the given collection of attributes.
    attributes->pushObject("CameraComponent", root);
    root->drop();

    // Return
    return true;
}

// Deserializes this object.
bool CameraComponent::onDeserialization(SerializedAttributes *attributes)
{
    // Retrieve root attribute from the collection.
    SerializedAttribute *rootAttribute = attributes->pullAttribute();

    if(rootAttribute == NULL || rootAttribute->getName() != "CameraComponent")
      return false;

    SerializedAttributes *root = reinterpret_cast<SerializedAttributes*>(rootAttribute->getObject());
    root->grab();

    rootAttribute->drop(); // We no longer need this attribute.

    // Process all attributes in the root collection.
    for(SerializedAttribute *attribute = root->pullAttribute(); attribute != NULL; attribute = root->pullAttribute())
    {
       // Deserialize attributes
       if(attribute->getName() == "SceneComponent")
       {
           // Rewrap attribute so that it can be deserialized properly.
           SerializedAttributes *wrappedAttribute = new SerializedAttributes();
           wrappedAttribute->pushAttribute(attribute);

           // Deserialize the component.
           SceneComponent::onDeserialization(wrappedAttribute);

           // Clean up.
           wrappedAttribute->drop();
       }

       else if(attribute->getName() == "asMainCamera")
       {
          if(attribute->getBool())
            setAsMainCamera();
       }

       else if(attribute->getName() == "aspectRatio")   setAspectRatio(attribute->getFloat());
       else if(attribute->getName() == "farValue")      setFarValue(attribute->getFloat());
       else if(attribute->getName() == "FOV")           setFOV(attribute->getFloat());
       else if(attribute->getName() == "nearValue")     setNearValue(attribute->getFloat());
       else if(attribute->getName() == "target")        setTarget(attribute->getVector3df());
       else if(attribute->getName() == "upVector")      setUpVector(attribute->getVector3df());

       // We no longer need a reference to the attribute.
       attribute->drop();
    }

    // We no longer need a reference to the root.
    root->drop();

    // We're done.
    return true;
}

// End of File
